#include "hashTable.h"

unsigned int fctHash(char * source)
{
	unsigned int i, hash=0;
	
	for(i=0;i<strlen(source);i++)
	{
		hash = hash * CST_HASH + source[i];
	}
	
	return hash % HASH_TABLE_SIZE;
}

void initHashTable()
{
	int i;
	for(i=0;i<HASH_TABLE_SIZE; i++)
	{
		hashTable[i]=NULL;
	}
}

int existeIndexHashTable(int indexHashTable)
{
	return hashTable[indexHashTable] != NULL;
}

int existeMotClefHashTable(char * clef)
{
	int indexHashTable = fctHash(clef);
	
	if(existeIndexHashTable(indexHashTable))
	{
		return existeChaineChaineEntreeNode(clef,recupLigneHashTable(indexHashTable));
	}
		
	return 0;
}

int existeMotClefHashTableHorsVide(char * clef)
{
	int indexHashTable = fctHash(clef);
	
	if(existeIndexHashTable(indexHashTable))
	{
		return existeChaineChaineEntreeNodeCibleNonNull(clef,recupLigneHashTable(indexHashTable));
	}
		
	return 0;
}

pChaineChaineEntreeNode recupLigneHashTable(int indexHashTable)
{
	return hashTable[indexHashTable];
}

entree consulteHashTable(char * clef)
{//suppose que le mot existe dans la table (non null)
	int indexHashTable = fctHash(clef);
	pChaineChaineEntreeNode ligne = recupLigneHashTable(indexHashTable);
	pChaineEntreeNode node = rechercheCibleChaineChaineEntreeNode(clef,ligne);
	
	return valeurChaineEntreeNode(node);
}

int entreeDejaDansBloc(char * nom)
{
	pPileChaineEntreeNode parcourt;
	//ligne + clef
	if(existeMotClefHashTableHorsVide(nom))
	{//il existe deja		
		parcourt = pileEntree;
		while(parcourt!=NULL && (pileBloc== NULL || valeurPileChaineEntreeNode(pileBloc) != valeurPileChaineEntreeNode(parcourt)))
		{
			if(parcourt == NULL)
			{
				printf("Erreur : entreeDejaDansBloc : la pile d'entree est null.\n");
				exit(-1);
			}
			
			if(strcmp(valeurChaineEntreeNode(valeurPileChaineEntreeNode(parcourt)).nom, nom) == 0)
			{
				return 1;
			}
			
			parcourt = nextPileChaineEntreeNode(parcourt);
		}
	}
	
	return 0;
}

void ajoutEntreeHashTable(entree element)
{
	int indexHashTable = fctHash(element.nom);
	int existeCCEN;
	int existeCCENNonNull;
	
	//ligne + clef + déjà dans bloc -> erreur
	if(entreeDejaDansBloc(element.nom))
	{
		printf("Erreur : ajoutEntreeHashTable : mot clef déjà définit dans bloc courant.\n");
		exit(-1);
	}
	
	pChaineEntreeNode chaine = NULL; //le meme id blocs parents
	pChaineChaineEntreeNode ligne = recupLigneHashTable(indexHashTable);
	
	existeCCENNonNull = existeChaineChaineEntreeNodeCibleNonNull(element.nom,ligne);
	existeCCEN = existeChaineChaineEntreeNode(element.nom,ligne);
		
	if(existeCCEN)
	{//la variable a déjà été déclarée
		//on ne peut changer le type d'une variable déjà déclarée (meme dans un bloc disjoint)
		if(consulteHashTable(element.nom).typeEntree != element.typeEntree)
		{
			printf("Erreur : ajoutEntreeHashTable : tentative d'ajout d'un identifiant dont le type est différent d'une déclaration d'un identifiant de meme non précédente\n");
			exit(-1);
		}
	}

	if(existeCCENNonNull)
	{//entree existe déjà dans un bloc parent 
		chaine = rechercheCibleChaineChaineEntreeNode(element.nom,ligne);
		
		//empilement de la valeur pour ne pas l'écraser
		ajoutPourGestionPile(element.nom);
	}		

	
	//ajout pile entree + maj chainesElement meme nom
	chaine = ajoutChaineEntreeNodeDebut(element, chaine);	
	pileEntree = ajoutPileChaineEntreeNodeDebut(chaine,pileEntree);

	//maj hashtable
	if(!existeCCEN)
	{//la clef n'existe pas dans la ligne
		ligne = ajoutChaineChaineEntreeNodeDebut(element.nom,chaine,ligne);
		hashTable[indexHashTable]=ligne;
		
		if(element.typeEntree !=  ENTREE_TYPEENTREE_FCT)
		{		
			ajoutVariableADeclarer(element);
		}
	}
	else
	{
		modifierCibleChaineChaineEntreeNode(element.nom, chaine, ligne);
	}
}

void entreeBloc()
{	
	if(pileChaineEntreeEstNull(pileEntree))
	{
		pileBloc = ajoutPileChaineEntreeNodeDebut(NULL, pileBloc);
	}
	else
	{
		pileBloc = ajoutPileChaineEntreeNodeDebut(valeurPileChaineEntreeNode(pileEntree), pileBloc);
	}
	
	marquerPourGestionPile();
}

void sortieBloc()
{
	if(pileBloc==NULL && pileEntree == NULL)
	{
		printf("Erreur : sortieBloc : pile bloc vide. Sortie bloc initiale impossible.\n");
		exit(-1);
	}
	
	//dépiler les entrees et maj table hash
	while(pileEntree!=NULL && (pileBloc== NULL || valeurPileChaineEntreeNode(pileBloc) != valeurPileChaineEntreeNode(pileEntree)))
	{
		if(pileEntree == NULL)
		{
			printf("Erreur : sortieBloc : la pile d'entree est null.\n");
			exit(-1);
		}
		
		pChaineEntreeNode node = valeurPileChaineEntreeNode(pileEntree);
		char * nom = valeurChaineEntreeNode(node).nom;
		int indexHashTable = fctHash(nom);
		pChaineChaineEntreeNode ligne = recupLigneHashTable(indexHashTable);
		
		//maj table hash
		if(nextChaineEntreeNode(node)==NULL)
		{//il n'y a pas d'autres elements dans les blocs parents
			//la variable est fantomisée
			fantomiserEntreeBlocCourant(nom);
		}
		else
		{			
			//maj liste element meme nom blocs parents
			node = suppressionChaineEntreeDebut(node);
			modifierCibleChaineChaineEntreeNode(nom,node,ligne);			
		}
		//depiler pile entree
		pileEntree = suppressionPileChaineEntreeDebut(pileEntree);
	}
	
	//dépiler bloc
	if(pileBloc != NULL)
	{
		pileBloc = suppressionPileChaineEntreeDebut(pileBloc);
	}
	
	//remise a jour des valeurs masquée par le bloc
	while(strcmp(pourGestionPile[nbPourGestionPile-1],"_marqueur") != 0)
	{
		suppressionPourGestionPile();
	}
	suppressionMarqueurPourGestionPile();
}

void majValeurEntiereConstanteEntreeBlocCourant(char * clef, int valeur)
{
	if(!existeMotClefHashTableHorsVide(clef))
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'existe pas.\n",clef);
		exit(-1);
	}
	
	pChaineChaineEntreeNode ligne = recupLigneHashTable(fctHash(clef));
	pChaineEntreeNode node = rechercheCibleChaineChaineEntreeNode(clef,ligne);
	entree element = valeurChaineEntreeNode(node);
	if(element.typeEntree != ENTREE_TYPEENTREE_CTE || element.valeur.type != ENTREE_TYPECTE_ENTIER)
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'est pas de type constante ou de type entier.\n",clef);
		exit(-1);
	}
	node->valeur.valeur.valeurIntBool = valeur;
}

void majValeurBoolConstanteEntreeBlocCourant(char * clef, int valeur)
{
	if(!existeMotClefHashTableHorsVide(clef))
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'existe pas.\n",clef);
		exit(-1);
	}
	
	pChaineChaineEntreeNode ligne = recupLigneHashTable(fctHash(clef));
	pChaineEntreeNode node = rechercheCibleChaineChaineEntreeNode(clef,ligne);
	entree element = valeurChaineEntreeNode(node);
	if(element.typeEntree != ENTREE_TYPEENTREE_CTE || element.valeur.type != ENTREE_TYPECTE_BOOL)
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'est pas de type constante ou de type bool.\n",clef);
		exit(-1);
	}
	node->valeur.valeur.valeurIntBool = valeur;	
}

void majValeurCharConstanteEntreeBlocCourant(char * clef, char * valeur)
{
	if(!existeMotClefHashTableHorsVide(clef))
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'existe pas.\n",clef);
		exit(-1);
	}
	
	pChaineChaineEntreeNode ligne = recupLigneHashTable(fctHash(clef));
	pChaineEntreeNode node = rechercheCibleChaineChaineEntreeNode(clef,ligne);
	entree element = valeurChaineEntreeNode(node);
	if(element.typeEntree != ENTREE_TYPEENTREE_CTE || element.valeur.type != ENTREE_TYPECTE_CHAR)
	{
		printf("Erreur : majValeurEntiereConstanteEntreeBlocCourant : %s n'est pas de type constante ou de type char *.\n",clef);
		exit(-1);
	}
	node->valeur.valeur.valeurChar = valeur;	
}

void fantomiserEntreeBlocCourant(char * clef)
{
	if(!existeMotClefHashTableHorsVide(clef))
	{
		printf("Erreur : fantomiserEntreeBlocCourant : %s n'existe pas.\n",clef);
		exit(-1);
	}
	
	pChaineChaineEntreeNode ligne = recupLigneHashTable(fctHash(clef));
	pChaineEntreeNode node = rechercheCibleChaineChaineEntreeNode(clef,ligne);
	node->valeur.idFantome = 1;
}

void afficheHashTable ()
{
	int compteur = 0,i;	
	pChaineChaineEntreeNode ligne ;
	
	for(i=0;i<HASH_TABLE_SIZE;i++)
	{
		if(existeIndexHashTable(i))
		{
			printf("ligne %d:\t",i);
			ligne = recupLigneHashTable(i);
			afficheChaineChaineEntree(ligne);
			compteur = compteur+1;
		}
	}
	
	printf("%d lignes trouvées\n",compteur);
}

int hashTableVide ()
{
	int i;
	for(i=0;i<HASH_TABLE_SIZE;i++)
	{
		if(!estVideChaineChaineEntree(hashTable[i]))
		{
			return 0;
		}
	}
	
	return 1;
}
